# -*- coding: utf-8 -*-

import pyglet
from pyglet import window, clock
from mMouse import cMouse, BUTTON_MIDDLE, BUTTON_LEFT, BUTTON_RIGHT
from pymunk.vec2d import Vec2d
from pyglet.window import key
from pyglet.gl.gl import glViewport, glMatrixMode, GL_PROJECTION, glLoadIdentity,\
    glOrtho, GL_MODELVIEW
from pyglet.event import EVENT_HANDLED
from mEngineConfig import cEngineConfig
from mKeyboard import cKeyboard, KEY_PRESSED
from mUtils import sort_dictionary
from mCamera import cCamera

class cEngine(object):

    """
     Esta clase se engarga de la gestion a mas alto nivel del motor del juego.
     
     Controla la pantalla, asi como las escenas disponibles en el juego.
     
     Es el encargado de actualizar la entrada del usuario (keyboard & mouse) asi como
     el punto de entrada para actualizar todos los elementos (update) y volcar la
     informacion a pantalla (render).

    :version:
    :author:
    """

    """ ATTRIBUTES

     Diccionario {int:cScene} con las diferentes escenas que componen el juego.

    scenes  (public)

     Escena actual que se esta ejecutando. De entre todas las escenas cargadas, solo
     hay una activa en cada momento.

    current_scene  (public)

     Configuracion con la que el motor ha sido iniciado o actualizado.

    original_config  (public)

     Contiene toda la informacion actual del motor. Puede haber diferencias con la
     configuracion requerida (original_config) si esta no se ha podido iniciar
     completamente debido a las restricciones del hardware/driver instalado.

    current_config  (public)

     Indica la velocidad relativa a la que se ejecuta el motor. La velocidad normal
     es 1.0 (100%), con un valor minimo de 0.01 (1%) y un valor maximo de 100.0
     (10000%). Esto se aplica de forma transparente a la escena activa en el momento
     de su actualizacion.

    time_scale  (public)

     Teclado utilizado para la actualizacion del estado de los elementos del juego.

    keyboard  (public)

     Raton utilizado para la actualizacion del estado de los elementos del juego.

    mouse  (public)

    """

    scenes = {}
    current_scene = None
    config = cEngineConfig() 
    keys = key.KeyStateHandler()
    keyboard = cKeyboard()
    mouse = cMouse()
    time_scale = 1.0
    current_log = 0
    log_batches = {}
    fps = 0

    #ventana del motor, iremos cambiando sus propiedades segun las selecciones del usuario
    window = window.Window(640,480,visible=False,caption="Loading...", vsync=0)
    camera = cCamera(window)
    #lista para dibujar mas rapidamente en pantalla
    #mas eficaz que ir llamando a los metodos render de cada elemento
    draw_batch = pyglet.graphics.Batch()

def initialize(config):
    """
     Inicia el motor con la configuracion deseada.

    @param cEngineConfig engine_config : Configuracion con la que se va a crear el motor.
    @return  :
    @author
    """
        
    cEngine.scenes = {}
    cEngine.current_scene = None
    cEngine.config = config
    cEngine.keyboard = cKeyboard()
    cEngine.mouse = cMouse()
    cEngine.time_scale = 1.0
    cEngine.current_log = 0
        
    #startup pyglet
    change_screen_config()
        
    #set up log batches
    if cEngine.config.debug:
        create_debug_info()
    
def create_debug_info():
    #with toggle log key we will iterate these batches
    step = 12
    font_size = 8
    #===============
    #-1 - always on debug
    #===============
    log_list = {}
    log_list["fps"]=pyglet.text.Label("fps: %r" % (cEngine.fps),font_name="Verdana", font_size=font_size, x=0, y=0, batch=cEngine.draw_batch)
        
    cEngine.log_batches[-1] = log_list
    #===============
    #1 - screen config
    #===============
    log_list = {}
    log_list["01_width"]=pyglet.text.Label("width: %r" % (cEngine.config.width),font_name="Verdana", font_size=font_size, x=0, batch=None)
    log_list["02_height"]=pyglet.text.Label("height: %r" % (cEngine.config.height),font_name="Verdana", font_size=font_size, x=0, batch=None)
    log_list["03_screen_width"]=pyglet.text.Label("screen_width: %r" % (cEngine.window.width),font_name="Verdana", font_size=font_size, x=0, batch=None)
    log_list["04_screen_height"]=pyglet.text.Label("screen_height: %r" % (cEngine.window.height),font_name="Verdana", font_size=font_size, x=0, batch=None)
    log_list["05_color"]=pyglet.text.Label("color: %r" % (cEngine.config.color),font_name="Verdana", font_size=font_size, x=0, batch=None)
    log_list["06_full_screen"]=pyglet.text.Label("full_screen: %r" % (cEngine.config.full_screen),font_name="Verdana", font_size=font_size, x=0, batch=None)
    log_list["07_debug"]=pyglet.text.Label("debug: %r" % (cEngine.config.debug),font_name="Verdana", font_size=font_size, x=0, batch=None)
    log_list["08_log"]=pyglet.text.Label("log: %r" % (cEngine.config.log),font_name="Verdana", font_size=font_size, x=0, batch=None)
    log_list["09_capture_mouse"]=pyglet.text.Label("capture_mouse: %r" % (cEngine.config.capture_mouse),font_name="Verdana", font_size=font_size, x=0, batch=None)
    log_list["10_caption"]=pyglet.text.Label("caption: %r" % (cEngine.config.caption),font_name="Verdana", font_size=font_size, x=0, batch=None)
        
    sorted_dict = sort_dictionary(log_list)
    sorted_dict.reverse()
    for current in xrange(len(sorted_dict)):
        sorted_dict[current].y = 12 + step*current
    
    cEngine.log_batches[1] = log_list
        
    #===============
    #2 - user input
    #===============
    log_list = {}
    log_list["01_buttons"]=pyglet.text.Label("buttons: %r %r %r" % (cEngine.mouse.get_pressed(BUTTON_LEFT),\
                                                               cEngine.mouse.get_pressed(BUTTON_MIDDLE),\
                                                               cEngine.mouse.get_pressed(BUTTON_RIGHT)), font_name="Verdana", font_size=font_size, x=0, batch=None)
    log_list["02_position"]=pyglet.text.Label("position: %r" % (cEngine.mouse.position),font_name="Verdana", font_size=font_size, x=0, batch=None)
    log_list["03_wheel"]=pyglet.text.Label("wheel_scroll: %r" % (cEngine.mouse.get_scroll()),font_name="Verdana", font_size=font_size, x=0, batch=None)

    sorted_dict = sort_dictionary(log_list)
    sorted_dict.reverse()
    for current in xrange(len(sorted_dict)):
        sorted_dict[current].y = 12 + step*current
    
    cEngine.log_batches[2] = log_list
        
def toggle_log():
    #if there is a log list loaded we have to clean that list
    if cEngine.current_log > 0:
        for item in cEngine.log_batches[cEngine.current_log].itervalues():
            item.delete()
    #if we are at the end of the list, reset current_log and return
    if(cEngine.current_log == len(cEngine.log_batches)-1):
        cEngine.current_log = 0
        return
    
    #else, advance one position and load the new logs
    cEngine.current_log += 1
    for item in cEngine.log_batches[cEngine.current_log].itervalues():
        item.text = ""
        item.batch = cEngine.draw_batch
    
def toggle_fullscreen():
    """
     Alterna entre ejecucion a pantalla completa o ejecucion en ventana.

    @return  :
    @author
    """
    cEngine.config.full_screen = not cEngine.config.full_screen
    change_screen_config()

def change_screen_config():
    """
     Actualiza la configuracion actual del motor.

    @param cEngineConfig engine_config : Nueva configuracion con la que se reiniciará el motor.
    @return  :
    @author
    """

    cEngine.window.set_fullscreen(cEngine.config.full_screen)

    if not cEngine.config.full_screen:
        cEngine.window.set_size(width=cEngine.config.width,height=cEngine.config.height)
    cEngine.window.set_caption(cEngine.config.caption)
           
    if cEngine.config.capture_mouse:
        cEngine.window.set_exclusive_mouse()
    cEngine.camera.hudProjection()
    
def add_scene(scene):
    """
     Añade una escena a la lista de escenas disponibles. Devuelve el id asignado a
     esa escena.

    @param cScene scene : Escena que se quiere añadir al motor del juego.
    @return int :
    @author
    """
    pass

def get_scene(scene_id):
    """
     Obtiene una escena determinada cargada en el motor.

    @param int scene_id : Id de la escena que se quiere obtener.
    @return cScene :
    @author
    """
    pass

def remove_scene(scene_id):
    """
     Elimina una escena determinada de la lista de escenas disponibles en el motor.

    @param int scene_id : Id de la escena que se quiere retirar.
    @return  :
    @author
    """
    pass

def switch_scene(scene_id):
    """
     Activa una escena determinada de la lista de escenas cargadas en el motor. Solo
     puede haber una escena activa en cada momento.

    @param int scene_id : Id de la escena que se quiere activar.
    @return  :
    @author
    """
    pass

def update(elapsed):
    """
     Metodo que inicia el bucle principal del juego. Dentro de este bucle se
     actualizan el teclado y el raton con la entrada del usuario. Se calcula el
     tiempo transcurrido (en segundos) desde la ultima actualizacion y se llama al
     metodo "update" de la escena activa en este momento.

    @return  :
    @author
    """
    
    #update user input
    cEngine.keyboard.update(cEngine.keys)
    cEngine.mouse.update()
    
    if cEngine.keyboard.get_key(key.ESCAPE) == KEY_PRESSED:
        exit()
    if cEngine.keyboard.get_key(key.F) == KEY_PRESSED:
        toggle_fullscreen()
    if cEngine.config.debug and cEngine.keyboard.get_key(key.TAB) == KEY_PRESSED:
        toggle_log()
        
    #if we are logging, update the log values
    if cEngine.config.debug:
        cEngine.fps = clock.get_fps()
        cEngine.log_batches[-1]["fps"].text = "fps: %r - current_log: %r" % (int(cEngine.fps), cEngine.current_log)      
        if cEngine.current_log == 1:
            update_log_screen()
        elif cEngine.current_log == 2:
            update_log_mouse()

def update_log_screen():
    cEngine.log_batches[1]["01_width"].text = "width: %r" % (cEngine.config.width)
    cEngine.log_batches[1]["02_height"].text = "height: %r" % (cEngine.config.height)
    cEngine.log_batches[1]["03_screen_width"].text = "screen_width: %r" % (cEngine.window.width)
    cEngine.log_batches[1]["04_screen_height"].text = "screen_height: %r" % (cEngine.window.height)
    cEngine.log_batches[1]["05_color"].text = "color: %r" % (cEngine.config.color)
    cEngine.log_batches[1]["06_full_screen"].text = "full_screen: %r" % (cEngine.config.full_screen)
    cEngine.log_batches[1]["07_debug"].text = "debug: %r" % (cEngine.config.debug)
    cEngine.log_batches[1]["08_log"].text = "log: %r" % (cEngine.config.log)
    cEngine.log_batches[1]["09_capture_mouse"].text = "capture_mouse: %r" % (cEngine.config.capture_mouse)
    cEngine.log_batches[1]["10_caption"].text = "caption: %r" % (cEngine.config.caption)

def update_log_mouse():
    cEngine.log_batches[2]["01_buttons"].text = "buttons: %r %r %r" % (cEngine.mouse.get_pressed(BUTTON_LEFT),\
                                                                  cEngine.mouse.get_pressed(BUTTON_MIDDLE),\
                                                                  cEngine.mouse.get_pressed(BUTTON_RIGHT))
    cEngine.log_batches[2]["02_position"].text = "position: %r" % (cEngine.mouse.position)
    cEngine.log_batches[2]["03_wheel"].text = "wheel_scroll: %r" % (cEngine.mouse.get_scroll())

def run():
    cEngine.window.set_location(cEngine.window.screen.width/2 - cEngine.window.width/2,\
                                cEngine.window.screen.height/2 - cEngine.window.height/2)    
    
    cEngine.window.push_handlers(cEngine.keys)

    pyglet.clock.schedule_interval(update, 1.0/60.0)

    cEngine.camera.hudProjection()

    cEngine.window.clear()
    cEngine.window.flip()

    cEngine.window.set_visible(True)
        
    pyglet.app.run()
        
@cEngine.window.event
def on_resize(width, height):
    glViewport(0, 0, width, height)

    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()

    # setup a fixed dimension orthographic projection
    # this avoids gameplay problems if the window resolution is changed
    glOrtho(0, 1024, 0, 768, -1, 1)

    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()
 
    # tell pyglet that we have handled the event, prevents the default handler from running
    return EVENT_HANDLED

@cEngine.window.event
def on_mouse_motion(x, y, dx, dy):
    cEngine.mouse.update_position(Vec2d(dx,dy))
        
@cEngine.window.event
def on_mouse_drag(x, y, dx, dy, buttons, modifiers):
    cEngine.mouse.update_position(Vec2d(dx, dy))
    cEngine.mouse.update_buttons(buttons)
     
@cEngine.window.event
def on_mouse_press(x, y, button, modifiers):
    cEngine.mouse.update_button_state(button, True)
                
@cEngine.window.event
def on_mouse_release(x, y, button, modifiers):
    cEngine.mouse.update_button_state(button, False)
                
@cEngine.window.event
def on_mouse_scroll(x, y, scroll_x, scroll_y):
    cEngine.mouse.update_wheel(scroll_y)

@cEngine.window.event
def on_draw():
    cEngine.window.clear()
    cEngine.draw_batch.draw()
